using System;
using CalcSharp.Core;
using CalcSharp.Core.Containers;
using System.Drawing;

namespace CalcSharp
{
    #region Logical Functions
    sealed class Logic
    {
        private Logic() { }
        #region Numbers
        public static double NOT(double num)
        {
            if (num - Math.Truncate(num) != 0)
            {
                byte[] data = BitConverter.GetBytes(num);
                for (int i = 0; i < 8; i++)
                {
                    data[i] = (byte)~data[i];
                }
                return BitConverter.ToDouble(data, 0);
            }
            else return Convert.ToDouble(~Convert.ToInt64(num));
        }
        public static double OR(double num1, double num2)
        {
            if ((num1 - Math.Truncate(num1) != 0) || (num2 - Math.Truncate(num2) != 0))
            {
                byte[] data = new byte[8];
                byte[] data1 = BitConverter.GetBytes(num1);
                byte[] data2 = BitConverter.GetBytes(num2);
                for (int i = 0; i < 8; i++)
                {
                    data[i] = (byte)(data1[i] | data2[i]);
                }
                return BitConverter.ToDouble(data, 0);
            }
            else return Convert.ToDouble(Convert.ToInt64(num1) | Convert.ToInt64(num2));
        }
        public static double NOR(double num1, double num2)
        {
            if ((num1 - Math.Truncate(num1) != 0) || (num2 - Math.Truncate(num2) != 0))
            {
                byte[] data = new byte[8];
                byte[] data1 = BitConverter.GetBytes(num1);
                byte[] data2 = BitConverter.GetBytes(num2);
                for (int i = 0; i < 8; i++)
                {
                    data[i] = (byte)~(data1[i] | data2[i]);
                }
                return BitConverter.ToDouble(data, 0);
            }
            else return Convert.ToDouble(~(Convert.ToInt64(num1) | Convert.ToInt64(num2)));
        }
        public static double AND(double num1, double num2)
        {
            if ((num1 - Math.Truncate(num1) != 0) || (num2 - Math.Truncate(num2) != 0))
            {
                byte[] data = new byte[8];
                byte[] data1 = BitConverter.GetBytes(num1);
                byte[] data2 = BitConverter.GetBytes(num2);
                for (int i = 0; i < 8; i++)
                {
                    data[i] = (byte)(data1[i] & data2[i]);
                }
                return BitConverter.ToDouble(data, 0);
            }
            return Convert.ToDouble(Convert.ToInt64(num1) & Convert.ToInt64(num2));
        }
        public static double NAND(double num1, double num2)
        {
            if ((num1 - Math.Truncate(num1) != 0) || (num2 - Math.Truncate(num2) != 0))
            {
                byte[] data = new byte[8];
                byte[] data1 = BitConverter.GetBytes(num1);
                byte[] data2 = BitConverter.GetBytes(num2);
                for (int i = 0; i < 8; i++)
                {
                    data[i] = (byte)~(data1[i] & data2[i]);
                }
                return BitConverter.ToDouble(data, 0);
            }
            return Convert.ToDouble(~(Convert.ToInt64(num1) & Convert.ToInt64(num2)));
        }
        public static double XOR(double num1, double num2)
        {
            if ((num1 - Math.Truncate(num1) != 0) || (num2 - Math.Truncate(num2) != 0))
            {
                byte[] data = new byte[8];
                byte[] data1 = BitConverter.GetBytes(num1);
                byte[] data2 = BitConverter.GetBytes(num2);
                for (int i = 0; i < 8; i++)
                {
                    data[i] = (byte)(data1[i] ^ data2[i]);
                }
                return BitConverter.ToDouble(data, 0);
            }
            return Convert.ToDouble(Convert.ToInt64(num1) ^ Convert.ToInt64(num2));
        }
        public static double EQ(double num1, double num2)
        {
            if ((num1 - Math.Truncate(num1) != 0) || (num2 - Math.Truncate(num2) != 0))
            {
                byte[] data = new byte[8];
                byte[] data1 = BitConverter.GetBytes(num1);
                byte[] data2 = BitConverter.GetBytes(num2);
                for (int i = 0; i < 8; i++)
                {
                    data[i] = (byte)((~data1[i] | data2[i]) & (~data2[i] | data1[i]));
                }
                return BitConverter.ToDouble(data, 0);
            }
            else
            {
                long n1 = Convert.ToInt64(num1);
                long n2 = Convert.ToInt64(num2);
                return ((~n1 | n2) & (n2 | n1));
            }
        }
        public static double SHL(double num, double digits)
        {
            if ((num - Math.Truncate(num) != 0))
            {
                byte[] data = BitConverter.GetBytes(num);
                ulong dt = BitConverter.ToUInt64(data, 0);
                dt = dt << Convert.ToInt32(digits);
                data = BitConverter.GetBytes(dt);
                return BitConverter.ToDouble(data, 0);
            }
            else return Convert.ToDouble(Convert.ToInt64(num) << Convert.ToInt32(digits));
        }
        public static double SHR(double num, double digits)
        {
            if ((num - Math.Truncate(num) != 0))
            {
                byte[] data = BitConverter.GetBytes(num);
                ulong dt = BitConverter.ToUInt64(data, 0);
                dt = dt >> Convert.ToInt32(digits);
                data = BitConverter.GetBytes(dt);
                return BitConverter.ToDouble(data, 0);
            }
            else return Convert.ToDouble(Convert.ToInt64(num) >> Convert.ToInt32(digits));
        }
        public static double BitCount(double num)
        {
            ulong m1 = 0x5555555555555555; //binary: 0101...
            ulong m2 = 0x3333333333333333; //binary: 00110011..
            ulong m4 = 0x0f0f0f0f0f0f0f0f; //binary:  4 zeros,  4 ones ...

            ulong numb;
            byte[] tmp;
            ulong cnt;

            if (num - Math.Truncate(num) != 0)
            {
                tmp = BitConverter.GetBytes(num);
                numb = BitConverter.ToUInt64(tmp, 0);
            }
            else numb = Convert.ToUInt64(num);

            numb -= (numb >> 1) & m1;             //put count of each 2 bits into those 2 bits
            numb = (numb & m2) + ((numb >> 2) & m2); //put count of each 4 bits into those 4 bits 
            numb = (numb + (numb >> 4)) & m4;        //put count of each 8 bits into those 8 bits 
            numb += numb >> 8;  //put count of each 16 bits into their lowest 8 bits
            numb += numb >> 16;  //put count of each 32 bits into their lowest 8 bits
            numb += numb >> 32;  //put count of each 64 bits into their lowest 8 bits
            cnt = (numb & 0x7f);
            return cnt;
        }
        #endregion

        #region Sets
        public static ASet NOT(ASet input)
        {
            ASet ret = SetFuncts.CreateSet(input.Count);
            for (int s = 0; s < input.Count; s++)
            {
                ret[s] = NOT(input[s]);
            }
            return ret;
        }
        public static ASet OR(ASet in1, ASet in2)
        {
            int limit;
            if (in1.Count > in2.Count) limit = in2.Count;
            else limit = in1.Count;
            ASet ret = SetFuncts.CreateSet(limit);
            for (int s = 0; s < limit; s++)
            {
                ret[s] = OR(in1[s], in2[s]);
            }
            return ret;
        }
        public static ASet NOR(ASet in1, ASet in2)
        {
            int limit;
            if (in1.Count > in2.Count) limit = in2.Count;
            else limit = in1.Count;
            ASet ret = SetFuncts.CreateSet(limit);
            for (int s = 0; s < limit; s++)
            {
                ret[s] = NOR(in1[s], in2[s]);
            }
            return ret;
        }
        public static ASet AND(ASet in1, ASet in2)
        {
            int limit;
            if (in1.Count > in2.Count) limit = in2.Count;
            else limit = in1.Count;
            ASet ret = SetFuncts.CreateSet(limit);
            for (int s = 0; s < limit; s++)
            {
                ret[s] = AND(in1[s], in2[s]);
            }
            return ret;
        }
        public static ASet NAND(ASet in1, ASet in2)
        {
            int limit;
            if (in1.Count > in2.Count) limit = in2.Count;
            else limit = in1.Count;
            ASet ret = SetFuncts.CreateSet(limit);
            for (int s = 0; s < limit; s++)
            {
                ret[s] = NAND(in1[s], in2[s]);
            }
            return ret;
        }
        public static ASet XOR(ASet in1, ASet in2)
        {
            int limit;
            if (in1.Count > in2.Count) limit = in2.Count;
            else limit = in1.Count;
            ASet ret = SetFuncts.CreateSet(limit);
            for (int s = 0; s < limit; s++)
            {
                ret[s] = XOR(in1[s], in2[s]);
            }
            return ret;
        }
        public static ASet EQ(ASet in1, ASet in2)
        {
            int limit;
            if (in1.Count > in2.Count) limit = in2.Count;
            else limit = in1.Count;
            ASet ret = SetFuncts.CreateSet(limit);
            for (int s = 0; s < limit; s++)
            {
                ret[s] = EQ(in1[s], in2[s]);
            }
            return ret;
        }
        public static ASet SHL(ASet input, double digits)
        {
            ASet ret = SetFuncts.CreateSet(input.Count);
            for (int s = 0; s < input.Count; s++)
            {
                ret[s] = SHL(input[s], digits);
            }
            return ret;
        }
        public static ASet SHR(ASet input, double digits)
        {
            ASet ret = SetFuncts.CreateSet(input.Count);
            for (int s = 0; s < input.Count; s++)
            {
                ret[s] = SHR(input[s], digits);
            }
            return ret;
        }
        public static ASet BitCount(ASet input)
        {
            ASet ret = SetFuncts.CreateSet(input.Count);
            for (int s = 0; s < input.Count; s++) ret[s] = BitCount(input[s]);
            return ret;
        }
        #endregion

        #region Matrix
        public static AMatrix NOT(AMatrix m)
        {
            AMatrix ret = MatrixFuncts.CreateMatrix(m.Rows, m.Columns);
            //DoubleMatrix ret = new DoubleMatrix(m.Rows, m.Columns);
            for (int i = 0; i < m.Rows; i++)
            {
                for (int j = 0; j < m.Columns; j++)
                {
                    ret[i, j] = NOT(m[i, j]);
                }
            }
            return ret;
        }
        public static AMatrix OR(AMatrix m1, AMatrix m2)
        {
            Point p = new Point();
            if ((m1.Rows * m1.Columns) < (m2.Rows * m2.Columns)) p = new Point(m1.Rows, m1.Columns);
            else p = new Point(m2.Rows, m2.Columns);
            AMatrix ret = MatrixFuncts.CreateMatrix(p.X, p.Y);
            for (int i = 0; i < p.X; i++)
            {
                for (int j = 0; j < p.Y; j++)
                {
                    ret[i, j] = OR(m1[i, j], m2[i, j]);
                }
            }
            return ret;
        }
        public static AMatrix NOR(AMatrix m1, AMatrix m2)
        {
            Point p = new Point();
            if ((m1.Rows * m1.Columns) < (m2.Rows * m2.Columns)) p = new Point(m1.Rows, m1.Columns);
            else p = new Point(m2.Rows, m2.Columns);
            AMatrix ret = MatrixFuncts.CreateMatrix(p.X, p.Y);
            for (int i = 0; i < p.X; i++)
            {
                for (int j = 0; j < p.Y; j++)
                {
                    ret[i, j] = NOR(m1[i, j], m2[i, j]);
                }
            }
            return ret;
        }
        public static AMatrix AND(AMatrix m1, AMatrix m2)
        {
            Point p = new Point();
            if ((m1.Rows * m1.Columns) < (m2.Rows * m2.Columns)) p = new Point(m1.Rows, m1.Columns);
            else p = new Point(m2.Rows, m2.Columns);
            AMatrix ret = MatrixFuncts.CreateMatrix(p.X, p.Y);
            for (int i = 0; i < p.X; i++)
            {
                for (int j = 0; j < p.Y; j++)
                {
                    ret[i, j] = AND(m1[i, j], m2[i, j]);
                }
            }
            return ret;
        }
        public static AMatrix NAND(AMatrix m1, AMatrix m2)
        {
            Point p = new Point();
            if ((m1.Rows * m1.Columns) < (m2.Rows * m2.Columns)) p = new Point(m1.Rows, m1.Columns);
            else p = new Point(m2.Rows, m2.Columns);
            AMatrix ret = MatrixFuncts.CreateMatrix(p.X, p.Y);
            for (int i = 0; i < p.X; i++)
            {
                for (int j = 0; j < p.Y; j++)
                {
                    ret[i, j] = NAND(m1[i, j], m2[i, j]);
                }
            }
            return ret;
        }
        public static AMatrix XOR(AMatrix m1, AMatrix m2)
        {
            Point p = new Point();
            if ((m1.Rows * m1.Columns) < (m2.Rows * m2.Columns)) p = new Point(m1.Rows, m1.Columns);
            else p = new Point(m2.Rows, m2.Columns);
            AMatrix ret = MatrixFuncts.CreateMatrix(p.X, p.Y);
            for (int i = 0; i < p.X; i++)
            {
                for (int j = 0; j < p.Y; j++)
                {
                    ret[i, j] = XOR(m1[i, j], m2[i, j]);
                }
            }
            return ret;
        }
        public static AMatrix EQ(AMatrix m1, AMatrix m2)
        {
            Point p = new Point();
            if ((m1.Rows * m1.Columns) < (m2.Rows * m2.Columns)) p = new Point(m1.Rows, m1.Columns);
            else p = new Point(m2.Rows, m2.Columns);
            AMatrix ret = MatrixFuncts.CreateMatrix(p.X, p.Y);
            for (int i = 0; i < p.X; i++)
            {
                for (int j = 0; j < p.Y; j++)
                {
                    ret[i, j] = EQ(m1[i, j], m2[i, j]);
                }
            }
            return ret;
        }
        public static AMatrix SHL(AMatrix m, double digits)
        {
            AMatrix ret = MatrixFuncts.CreateMatrix(m.Rows, m.Columns);
            //DoubleMatrix ret = new DoubleMatrix(m.Rows, m.Columns);
            for (int i = 0; i < m.Rows; i++)
            {
                for (int j = 0; j < m.Columns; j++)
                {
                    ret[i, j] = SHL(m[i, j], digits);
                }
            }
            return ret;
        }
        public static AMatrix SHR(AMatrix m, double digits)
        {
            AMatrix ret = MatrixFuncts.CreateMatrix(m.Rows, m.Columns);
            //DoubleMatrix ret = new DoubleMatrix(m.Rows, m.Columns);
            for (int i = 0; i < m.Rows; i++)
            {
                for (int j = 0; j < m.Columns; j++)
                {
                    ret[i, j] = SHR(m[i, j], digits);
                }
            }
            return ret;
        }

        public static AMatrix BitCount(AMatrix m)
        {
            AMatrix ret = MatrixFuncts.CreateMatrix(m.Rows, m.Columns);
            for (int i = 0; i < m.Rows; i++)
            {
                for (int j = 0; j < m.Columns; j++)
                {
                    ret[i, j] = BitCount(m[i, j]);
                }
            }
            return ret;
        }

        #endregion

    }
    #endregion
}